package com.css.upms.biz.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.css.common.core.constent.CommonConstant;
import com.css.common.core.utils.R;
import com.css.common.core.utils.UUIDUtils;
import com.css.common.security.utils.SecurityUtil;
import com.css.upms.api.dto.MenuTree;
import com.css.upms.api.entity.SysMenu;
import com.css.upms.api.entity.SysRoleMenu;
import com.css.upms.api.entity.SysUserRole;
import com.css.upms.api.vo.MenuVo;
import com.css.upms.api.vo.TreeUtil;
import com.css.upms.biz.mapper.SysMenuMapper;
import com.css.upms.biz.mapper.SysRoleMenuMapper;
import com.css.upms.biz.mapper.SysUserRoleMapper;
import com.css.upms.biz.service.SysMenuService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static org.apache.commons.collections4.CollectionUtils.isEmpty;

/**
 * 菜单权限表
 *
 * @author hanyx
 * @date 2019-05-03 01:49:04
 */
@Service("sysMenuService")
@AllArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    private final SysRoleMenuMapper sysRoleMenuMapper;
    private final SysUserRoleMapper sysUserRoleMapper;

    /**
     * 菜单权限表简单分页查询
     *
     * @param sysMenu 菜单权限表
     * @return
     */
    @Override
    public IPage<SysMenu> getSysMenuPage(Page<SysMenu> page, SysMenu sysMenu) {
        return baseMapper.getSysMenuPage(page, sysMenu);
    }

    @Override
    @Cacheable(value = "menu_details", key = "#roleId  + '_menu'")
    public List<MenuVo> getMenuByRoleId(String roleId) {
        List<MenuVo> menuVos = baseMapper.listMenusByRoleId(roleId);
        return menuVos;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "menu_details", allEntries = true)
    public R removeMenuById(String id) {
        // 查询父节点为当前节点的节点
        List<SysMenu> menuList = this.list(Wrappers.<SysMenu>query()
                .lambda().eq(SysMenu::getParentId, id));
        if (CollectionUtils.isNotEmpty(menuList)) {
            return R.buildError("菜单含有下级不能删除");
        }

        sysRoleMenuMapper.delete(Wrappers.<SysRoleMenu>query()
                .lambda().eq(SysRoleMenu::getMenuId, id));

        //删除当前菜单及其子菜单
        return new R(this.removeById(id));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "menu_details", allEntries = true)
    public R removeMenuByIds(String ids) {
        Arrays.stream(ids.split(",")).forEach(menuId -> {

            // 查询是否是父节点
            List<SysMenu> menuList = this.list(Wrappers.<SysMenu>query()
                    .lambda().eq(SysMenu::getParentId, menuId));
            // 空时表示是子节点 可以删除menu表中菜单
            if (CollectionUtils.isEmpty(menuList)) {
                this.removeById(menuId);
            }

            // 角色关联权限表是删除父节点
            sysRoleMenuMapper.delete(Wrappers.<SysRoleMenu>query()
                    .lambda().eq(SysRoleMenu::getMenuId, menuId));
        });
        return new R(Boolean.TRUE);
    }

    @Override
    @CacheEvict(value = "menu_details", allEntries = true)
    public R updateMenuById(SysMenu sysMenu) {
        // 修改时父节点不能选择自己
        if (StringUtils.equals(sysMenu.getParentId(), sysMenu.getMenuId())) {
            return R.buildError("父节点不能选择自己");
        }
        if (StringUtils.isBlank(sysMenu.getParentId())) {
            sysMenu.setParentId(CommonConstant.ROOT_NODE_VAL);
        }
        sysMenu.setModifyTime(LocalDateTime.now());
        sysMenu.setModifyBy(SecurityUtil.getUser().getId());
        return new R(this.updateById(sysMenu));
    }

    /**
     * 新建菜单
     *
     * @param menu
     * @return
     */
    @Override
    public R saveMenu(SysMenu menu) {
        String id = menu.getMenuId();
        if (id != null) {
            SysMenu vo = baseMapper.selectById(id);
            if (vo != null) {
                return R.buildError("数据已存在");
            }
        }
        menu.setCreateTime(LocalDateTime.now());
        menu.setCreateBy(SecurityUtil.getUser().getId());
        menu.setMenuId(id == null ? UUIDUtils.get32UUID() : id);
        return R.builder().data(this.save(menu)).build();
    }

    /**
     * 获取指定角色菜单权限信息 - 树结构
     *
     * @return
     */
    @Override
    public R getMenuTreeByRole(String roleIds) {
        List<String> roleList = Arrays.stream(roleIds.split(",")).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(roleList)) {
            return R.buildSuccess(Boolean.FALSE);
        }
        List<MenuTree> result = TreeUtil.buildByRecursive(getMenuListByRoleId(roleList), CommonConstant.ROOT_NODE_VAL);
        return R.buildSuccess(result);
    }

    /**
     * 获取当前用户菜单权限信息 - 树结构
     *
     * @return
     */
    @Override
    public List<MenuTree> getUserMenuTree() {
        return TreeUtil.buildByRecursive(getUserMenuList(), CommonConstant.ROOT_NODE_VAL);
    }


    /**
     * 是针对前段的list集合
     *
     * @return
     */
    @Override
    public List<MenuTree> getCurrentUserMenuTree() {
        List<SysUserRole> userRoleList = sysUserRoleMapper.selectList(Wrappers.<SysUserRole>query()
                .lambda().eq(SysUserRole::getUserId, SecurityUtil.getUser().getId()));
        if (isEmpty(userRoleList)) {
            new RuntimeException("用户没有角色");
        }

        // 获取符合条件的菜单
        List<MenuVo> result = baseMapper.listMenusByRoleIds(userRoleList.stream().map(userRole -> userRole.getRoleId()).collect(Collectors.toList()));
        return result.stream().map(MenuTree::new).collect(Collectors.toList());
    }

    /**
     * 获取当前用户菜单权限信息 - 集合数据结构
     *
     * @return
     */
    @Override
    public List<MenuTree> getMenuListByRoleId(List<String> roleIds) {
        // 获取符合条件的菜单
        List<MenuTree> menuTreeList = new ArrayList<>();
        Set<String> all = new HashSet<>();
        roleIds.forEach(roleId -> {
            List<String> menuList = sysRoleMenuMapper.selectList(Wrappers.<SysRoleMenu>query()
                    .lambda().eq(SysRoleMenu::getRoleId, roleId))
                    .stream().map(roleMenu -> roleMenu.getMenuId()).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(menuList)) {
                all.addAll(menuList);
            }
        });

        // 无菜单数据退出执行
        if (CollectionUtils.isEmpty(all)) {
            return menuTreeList;
        }

        // 构建父节点菜单集合
        Set<String> parentIds = new HashSet<>();
        Map<String, String> menuGroup = groupMenu(this.baseMapper.listAllMenu());
        all.forEach(menuId -> TreeUtil.parentNodeIds(menuId, parentIds, menuGroup));
        if (CollectionUtils.isNotEmpty(parentIds)) {
            all.addAll(parentIds);
        }

        menuTreeList = this.listByIds(all).stream()
                .filter(menuVo -> CommonConstant.MENU.equals(menuVo.getMenuType()))
                .map(MenuTree::new)
                .sorted(Comparator.comparingInt(MenuTree::getSort))
                .collect(Collectors.toList());
        return menuTreeList;
    }

    /**
     * 获取当前用户菜单权限信息 - 集合数据结构
     *
     * @return
     */
    @Override
    public List<MenuTree> getUserMenuList() {
        return getMenuListByRoleId(SecurityUtil.getRoles());
    }

    private Map<String, String> groupMenu(List<MenuVo> voList) {
        Map<String, String> parentMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(voList)) {
            for (MenuVo vo : voList) {
                parentMap.put(vo.getMenuId(), vo.getParentId());
            }
        }
        return parentMap;
    }

}
